fix: security writes stripped inherited members from access rules (#758, #765) - #81
Merged
Conversation
…ndixlabs#758, mendixlabs#765) Mendix models inheritance across multiple tables: a child adds attributes to the parent's, and all of the parent's are members of the child. An access rule must therefore carry a MemberAccess entry for every member — own AND inherited — or Mendix reports CE0066 "Entity access is out of date". Both the GRANT builder and ReconcileMemberAccesses enumerated only entity.Attributes. Two consequences, and the second explains why the first could not be worked around: * GRANT naming an inherited member produced no entry at all, while reporting success. * Reconciliation runs immediately after every GRANT, and on any write touching the module. An inherited reference is qualified against the entity that DECLARES it, so it never matched the child's own attribute list and was deleted as stale — removing, in the same command, what the grant had just written. That is why REVOKE + GRANT never repaired a damaged rule. The damage was masked: mx check reports CE0066 and stops, hiding the CE2729 "No read access to attribute" cascade until Studio Pro's Update security is clicked, so CLI-only workflows shipped it undetected. Two facts were established against mx check rather than inferred: 1. An inherited member's reference must be qualified against its declaring entity. Sec758.Base.SharedField validates clean; the child-qualified Sec758.Item.SharedField is CE1613 "The selected attribute no longer exists". mxcli wrote the child form. This is the same rule the change-object writer needs (mendixlabs#451). 2. System.User's members are the exception. Entities specialising it are user entities whose platform members Mendix manages: listing them turns a clean rule into CE0066 — confirmed on Mendix's own Administration.Account and on a fresh specialisation — while omitting System.FileDocument's six members is CE0066 until all are present. Fixed: * EntityMembers walks the generalization chain, qualifying each member against its declaring entity and excluding System.User's platform members. The GRANT builder uses it, and now rejects a named member that matched nothing instead of dropping it in silence. * Reconciliation strips only a reference qualified to the entity itself. An ancestor may live in another module or in System, neither of which is loaded at that layer, so an inherited reference cannot be validated there — it is preserved rather than deleted. Applied to both engines. Verified end-to-end on a real 11.12.2 project carrying all three specialisation shapes at once — same-module ancestor, System.FileDocument, and System.User — mx check reports 0 errors, and describe round-trips both members of the mixed entity. All three guards mutation-checked. Refs mendixlabs#758, mendixlabs#765
Nothing about entity inheritance appeared in any security doc, even though a specialized entity's access rule must cover its inherited members and getting it wrong is CE0066. Added to each surface the story touches: - mxcli syntax security.entity-access — an "Inherited members" block plus examples for a same-module ancestor and System.FileDocument - skills/mendix/manage-security.md — worked example, the None-rights detail, the new unknown-member error, and the System.User exception - skills/mendix/generate-domain-model.md — a pointer from EXTENDS, where a reader meets inheritance first - docs-site security/grant.md — the same as reference prose - MDL_QUICK_REFERENCE.md — the grant-entity-access row Covers what mendixlabs#758/mendixlabs#765 made work: inherited members are named exactly like the entity's own, READ */WRITE * include them, unmatched names are an error rather than a silent skip, and entities extending System.User must not grant their inherited platform members.
ako
pushed a commit
that referenced
this pull request
Aug 2, 2026
The doctype example granted read on SecTest.Customer (Notes), but the entity only declares Name, Email and IsActive. Before mendixlabs#758 an unmatched member name was dropped in silence, so the grant did nothing and the script still passed; with that silence replaced by an error the example fails, and the integration tier caught it. The example is what is wrong: its own comment says "adding Notes access preserves existing Name and Email", so it always meant to demonstrate an additive grant on a third attribute. Declaring Notes makes it do that. Fixes the build-and-test failure on main introduced by #81.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes mendixlabs/mxcli#758 and the root cause in #765.
Problem
Mendix models inheritance across multiple tables: a child adds attributes to the parent's, and all of the parent's are members of the child. An access rule must therefore carry a MemberAccess entry for every member — own and inherited — or Mendix reports CE0066 "Entity access is out of date".
Both the GRANT builder and
ReconcileMemberAccessesenumerated onlyentity.Attributes. Two consequences, and the second explains why the first couldn't be worked around:GRANTnaming an inherited member produced no entry at all, while reporting success.GRANT, and on any write touching the module. An inherited reference is qualified against the entity that declares it, so it never matched the child's own attribute list and was deleted as stale — removing, in the same command, what the grant had just written correctly.That last point is the whole issue: it is why
REVOKE+GRANTnever repaired a damaged rule, and it is not visible from the grant code alone.The damage was masked.
mx checkreports CE0066 and stops, hiding the CE2729 "No read access to attribute" cascade until Studio Pro's Update security is clicked — so CLI-only workflows shipped it undetected.Two facts, established against
mx checkrather than inferred1. An inherited reference is qualified against its declaring entity.
mx checkSec758.Item.SharedField(child-qualified — what mxcli wrote)Sec758.Base.SharedField(declaring entity)This is the same rule the change-object writer needs (mendixlabs#451).
2.
System.Useris the exception. Entities specialising it are user entities whose platform members Mendix manages:Sec758.Base(same module)Sec758.BaseSystem.FileDocumentSystem.FileDocumentSystem.UserSystem.UserVerified on Mendix's own
Administration.Accountand on a fresh specialisation, with a no-op-rewrite control confirming that merely touching the module doesn't itself trigger CE0066.Approach
EntityMembers(new,mdl/executor/entity_hierarchy.go) walks the generalization chain, qualifies each member against its declaring entity, handles child-shadows-ancestor, guards against cycles, and excludesSystem.User's platform members.mdl/backend/modelsdkandsdk/mpr), which had separate copies of the same defect.Verification
End-to-end on a real 11.12.2 project carrying all three specialisation shapes at once — same-module ancestor,
System.FileDocument, andSystem.User:Stored refs are
Sec758.Item.OwnField+Sec758.Base.SharedField;Attachmentgets 6System.FileDocumententries;Employeegets 0System.Userentries.All three guards mutation-checked — removing the chain walk, the
System.Userexclusion, or the reconciler's ownership check each reproduces the reported symptom. Full./...suite green.Repro script:
mdl-examples/bug-tests/758-inherited-member-access.mdl.Scope
This fixes mendixlabs#758 and the security half of the mendixlabs#765 umbrella. mendixlabs#765 also covers mendixlabs#703 (import/export mapping skips inherited attributes) — untouched here — and mendixlabs#451, whose declaring-entity rule the microflow path already implements via
resolveAttributeInEntityHierarchy.EntityMembersis deliberately ctx-based and reusable, so mendixlabs#703 can adopt it.Generalisable lesson (recorded in the symptom table)
When a post-write reconcile pass validates against a narrower model than the writer used, it will quietly undo correct writes. Check what runs after a write before concluding the writer is at fault — the grant code here was fixed first and appeared to change nothing.
Generated by Claude Code